package com.quanz.ckgl.tools;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.StringUtils;
import org.crank.crud.criteria.Between;
import org.crank.crud.criteria.Comparison;
import org.crank.crud.criteria.Criterion;
import org.crank.crud.criteria.Group;
import org.crank.crud.criteria.Junction;
import org.crank.crud.criteria.OrderBy;
import org.crank.crud.criteria.OrderDirection;
import org.crank.crud.criteria.PropertyExpression;
import org.crank.crud.criteria.SQLCriterion;
import org.hibernate.criterion.Restrictions;

/**
 * 用于将dwr/json传递的查询约束条件转换为GenericDao约束对象的工具类
 * 
 * @author <a href="mailto:quanz@rizon.com.cn">James Quan</a>
 * @version Created at 2008-5-20 下午01:53:15
 */
public abstract class ExpressionUtils {

	/**
	 * 返回与界面查询相对应的Criterion对象
	 * 
	 * @param expression
	 *            约束表达式
	 * @return
	 */
	public static Criterion getCriteria(Expression expression) {
		return getCriteria(expression, null);
	}

	/**
	 * 返回与界面查询相对应的Criterion对象
	 * 
	 * @param expression
	 *            约束表达式
	 * @param fieldMapping
	 *            字段映射对应表
	 * @return
	 */
	public static Criterion getCriteria(Expression expression,
			Map<String, String> fieldMapping) {
		String name = expression.getName();
		if (fieldMapping != null && fieldMapping.containsKey(name)) {
			name = fieldMapping.get(name);
		}
		String type = expression.getType();
		if (Expression.EXPRESSION_TYPE_VALUE.equals(type)) {
			String op = expression.getOp();
			Object value = expression.getValue();
			if (Operator.EQ.equals(op)) {
				return Comparison.eq(name, value);
			} else if (Operator.NE.equals(op)) {
				return Comparison.ne(name, value);
			} else if (Operator.LE.equals(op)) {
				return Comparison.le(name, value);
			} else if (Operator.GE.equals(op)) {
				return Comparison.ge(name, value);
			} else if (Operator.GT.equals(op)) {
				return Comparison.gt(name, value);
			} else if (Operator.LT.equals(op)) {
				return Comparison.lt(name, value);
			} else if (Operator.BETWEEN.equals(op)) {
				Object[] v = (Object[]) value;
				return Comparison.between(name, v[0], v[1]);
			} else if (Operator.IN.equals(op)) {
				return Comparison.in(name, value);
			} else if (Operator.LIKE.equals(op)) {
				return Comparison.like(name, (String) value);
			} else if (Operator.ILIKE.equals(op)) {
				return Comparison.ilike(name, (String) value);
			} else if (Operator.IS_NULL.equals(op)) {
				return Comparison.isNull(name);
			} else if (Operator.IS_NOT_NULL.equals(op)) {
				return Comparison.isNotNull(name);
			} else {
				throw new IllegalArgumentException(
						"unknown value expression operator:" + op);
			}
		} else if (Expression.EXPRESSION_TYPE_GROUP.equals(type)) {
			String op = expression.getOp();
			Object value = expression.getValue();
			if (Operator.AND.equals(op)) {
				Expression[] exps = (Expression[]) value;
				Criterion[] criteria = new Criterion[0];
				if (exps != null) {
					criteria = new Criterion[exps.length];
					for (int i = 0; i < exps.length; i++) {
						criteria[i] = getCriteria(exps[i], fieldMapping);
					}
				}
				return Group.and(criteria);
			} else if (Operator.OR.equals(op)) {
				Expression[] exps = (Expression[]) value;
				Criterion[] criteria = new Criterion[0];
				if (exps != null) {
					criteria = new Criterion[exps.length];
					for (int i = 0; i < exps.length; i++) {
						criteria[i] = getCriteria(exps[i], fieldMapping);
					}
				}
				return Group.or(criteria);
			} else {
				throw new IllegalArgumentException(
						"unknown group expression operator:" + op);
			}
		} else if (Expression.EXPRESSION_TYPE_PROPERTY.equals(type)) {
			String op = expression.getOp();
			String value = expression.getStringValue();
			Operator o = null;
			if (Operator.EQ.equals(op)) {
				return new PropertyExpression(name, value,
						org.crank.crud.criteria.Operator.EQ);
			} else if (Operator.NE.equals(op)) {
				return new PropertyExpression(name, value,
						org.crank.crud.criteria.Operator.NE);
			} else if (Operator.LE.equals(op)) {
				return new PropertyExpression(name, value,
						org.crank.crud.criteria.Operator.LE);
			} else if (Operator.GE.equals(op)) {
				return new PropertyExpression(name, value,
						org.crank.crud.criteria.Operator.GE);
			} else if (Operator.GT.equals(op)) {
				return new PropertyExpression(name, value,
						org.crank.crud.criteria.Operator.GT);
			} else if (Operator.LT.equals(op)) {
				return new PropertyExpression(name, value,
						org.crank.crud.criteria.Operator.LT);
			} else {
				throw new IllegalArgumentException(
						"unknown property expression operator:" + op);
			}
		} else if (Expression.EXPRESSION_TYPE_SQL.equals(type)) {
			String sql = expression.getStringValue();
			return new SQLCriterion(sql);
		} else {
			throw new IllegalArgumentException("unknown property type:" + type);
		}
	}

	/**
	 * 将界面查询的排序设置信息转换为对应的OrderBy对象集合
	 * 
	 * @param orderBy
	 *            从界面传来的排序设置信息，格式为“字段1 排序方式,字段2 排序方式...”，排序方式是ASC/或DESC
	 * @return
	 */
	public static OrderBy[] getOrderBy(String orderBy) {
		return getOrderBy(orderBy, null);
	}

	/**
	 * 将界面的排序设置信息转换为对应的OrderBy对象集合
	 * 
	 * @param orderBy
	 *            从界面传来的排序设置信息，格式为“字段1 排序方式,字段2 排序方式...”，排序方式是ASC/或DESC
	 * @param fieldMapping
	 *            字段映射对应表
	 * @return
	 */
	public static OrderBy[] getOrderBy(String orderBy,
			Map<String, String> fieldMapping) {
		String[] orders = StringUtils.split(orderBy, ',');
		if (orders == null || orders.length == 0) {
			return new OrderBy[0];
		}
		OrderBy[] result = new OrderBy[orders.length];
		for (int i = 0; i < orders.length; i++) {
			String order = StringUtils.trim(orders[i]);
			if (StringUtils.isEmpty(order)) {
				throw new IllegalArgumentException("orderBy is null!");
			}
			int pos = order.indexOf(' ');
			String field = pos == -1 ? order : order.substring(0, pos);
			if (fieldMapping != null && fieldMapping.containsKey(field)) {
				field = fieldMapping.get(field);
			}
			String dir = pos == -1 ? "ASC" : StringUtils.trim(order
					.substring(pos + 1));
			result[i] = "ASC".equalsIgnoreCase(dir) ? OrderBy.asc(field)
					: OrderBy.desc(field);
		}
		return result;
	}

	/**
	 * 返回与指定的查询约束相对应的hibernate查询对象
	 * 
	 * @param criterion
	 *            查询约束
	 * @param alias
	 *            需要建立别名映射的属性
	 * @return
	 */
	public static org.hibernate.criterion.Criterion getHibernateCriterion(
			Criterion criterion, Set<String> alias) {
		if (criterion instanceof Comparison) {
			org.crank.crud.criteria.Operator operator = ((Comparison) criterion)
					.getOperator();
			String name = ((Comparison) criterion).getName();
			int pos = name.lastIndexOf('.');
			if (pos != -1) {
				String path = name.substring(0, pos);
				if (!alias.contains(path)) {
					alias.add(path);
				}
			}
			Object value = ((Comparison) criterion).getValue();
			switch (operator) {
			case EQ:
				return Restrictions.eq(name, value);
			case NE:
				return Restrictions.ne(name, value);
			case LE:
				return Restrictions.le(name, value);
			case GE:
				return Restrictions.ge(name, value);
			case GT:
				return Restrictions.gt(name, value);
			case LT:
				return Restrictions.lt(name, value);
			case BETWEEN:
				return Restrictions.between(name, value,
						((Between) criterion).getValue2());
			case IN:
				if (value instanceof Collection) {
					return Restrictions.in(name, (Collection) value);
				} else if (value instanceof Object[]) {
					return Restrictions.in(name, (Object[]) value);
				} else {
					return Restrictions.in(name, new Object[] { value });
				}
			case LIKE:
				return Restrictions.like(name, value);
			case LIKE_START:
				return Restrictions.like(name, value == null ? "%" : value
						+ "%");
			case LIKE_END:
				return Restrictions.like(name, value == null ? "%" : "%"
						+ value);
			case LIKE_CONTAINS:
				return Restrictions.like(name, value == null ? "%" : "%"
						+ value + "%");
			case ILIKE:
				return Restrictions.ilike(name, value);
			case IS_NULL:
				return Restrictions.isNull(name);
			case IS_NOT_NULL:
				return Restrictions.isNotNull(name);
			default:
				throw new IllegalArgumentException("unknown operator "
						+ operator);
			}
		} else if (criterion instanceof Group) {
			Junction junction = ((Group) criterion).getJunction();
			switch (junction) {
			case AND: {
				Object[] criteria = IteratorUtils.toArray(((Group) criterion)
						.iterator());
				if (criteria == null || criteria.length == 0) {
					return null;
				}
				if (criteria.length == 1) {
					if (!(criteria[0] instanceof Criterion)) {
						throw new IllegalArgumentException(
								"expected a Criterion, actualy is "
										+ criteria[0]);
					}
					return getHibernateCriterion((Criterion) criteria[0], alias);
				} else {
					if (!(criteria[0] instanceof Criterion)) {
						throw new IllegalArgumentException(
								"expected a Criterion, actualy is "
										+ criteria[0]);
					}
					org.hibernate.criterion.Criterion lhs = getHibernateCriterion(
							(Criterion) criteria[0], alias);
					for (int i = 1; i < criteria.length; i++) {
						if (!(criteria[i] instanceof Criterion)) {
							throw new IllegalArgumentException(
									"expected a Criterion, actualy is "
											+ criteria[0]);
						}
						org.hibernate.criterion.Criterion rhs = getHibernateCriterion(
								(Criterion) criteria[i], alias);
						lhs = Restrictions.and(lhs, rhs);
					}
					return lhs;
				}
			}
			case OR: {
				Object[] criteria = IteratorUtils.toArray(((Group) criterion)
						.iterator());
				if (criteria == null || criteria.length == 0) {
					throw new IllegalArgumentException(
							"criteria group is empty!");
				}
				if (criteria.length == 1) {
					if (!(criteria[0] instanceof Criterion)) {
						throw new IllegalArgumentException(
								"expected a Criterion, actualy is "
										+ criteria[0]);
					}
					return getHibernateCriterion((Criterion) criteria[0], alias);
				} else {
					if (!(criteria[0] instanceof Criterion)) {
						throw new IllegalArgumentException(
								"expected a Criterion, actualy is "
										+ criteria[0]);
					}
					org.hibernate.criterion.Criterion lhs = getHibernateCriterion(
							(Criterion) criteria[0], alias);
					for (int i = 1; i < criteria.length; i++) {
						if (!(criteria[i] instanceof Criterion)) {
							throw new IllegalArgumentException(
									"expected a Criterion, actualy is "
											+ criteria[0]);
						}
						org.hibernate.criterion.Criterion rhs = getHibernateCriterion(
								(Criterion) criteria[i], alias);
						lhs = Restrictions.or(lhs, rhs);
					}
					return lhs;
				}
			}
			default:
				throw new IllegalArgumentException("unknown junction "
						+ junction);
			}
		} else if (criterion instanceof PropertyExpression) {
			org.crank.crud.criteria.Operator operator = ((PropertyExpression) criterion)
					.getOperator();
			String propertyName = ((PropertyExpression) criterion).getName();
			String otherPropertyName = (String) ((PropertyExpression) criterion)
					.getOtherName();
			switch (operator) {
			case EQ:
				return Restrictions.eqProperty(propertyName, otherPropertyName);
			case NE:
				return Restrictions.neProperty(propertyName, otherPropertyName);
			case LE:
				return Restrictions.leProperty(propertyName, otherPropertyName);
			case GE:
				return Restrictions.geProperty(propertyName, otherPropertyName);
			case GT:
				return Restrictions.gtProperty(propertyName, otherPropertyName);
			case LT:
				return Restrictions.ltProperty(propertyName, otherPropertyName);
			default:
				throw new IllegalArgumentException("unknown operator "
						+ operator);
			}
		} else if (criterion instanceof SQLCriterion) {
			String sql = ((SQLCriterion) criterion).getSql();
			return Restrictions.sqlRestriction(sql);
		} else {
			throw new IllegalArgumentException("unsupported criterion "
					+ criterion);
		}
	}

	/**
	 * 依据字段映射表将查询约束转化为实际查询用的约束对象
	 * 
	 * @param expression
	 *            查询定义
	 * @param mapping
	 *            条件字段与实际查询字段的映射对应表
	 * @return 将查询字段按照映射表进行转换后的约束对象
	 */
	public static Criterion getMappedCriteria(Criterion expression,
			Map<String, String> mapping) {
		if (expression == null) {
			return null;
		}
		if (expression instanceof Comparison) {
			Comparison c = (Comparison) expression;
			String name = c.getName();
			if (mapping != null && mapping.containsKey(name)) {
				name = mapping.get(name);
			}
			if (c instanceof Between) {
				return new Between(name, c.getValue(),
						((Between) c).getValue2(), c.isAlias());
			} else {
				return new Comparison(name, c.getOperator(), c.getValue(),
						c.isAlias());
			}
		} else if (expression instanceof Group) {
			Group g = (Group) expression;
			Group result = new Group();
			result.setJunction(g.getJunction());
			for (Iterator<Criterion> iterator = g.iterator(); iterator
					.hasNext();) {
				result.add(getMappedCriteria(iterator.next(), mapping));
			}
			return result;
		} else {
			return expression;
		}
	}

	/**
	 * 依据字段映射表将排序规则转化为实际使用的排序对象
	 * 
	 * @param orderBy
	 *            排序规则
	 * @param mapping
	 *            排序字段与实际字段的映射对应表
	 * @return 将排序字段按照映射表进行转换后的排序对象
	 */
	public static OrderBy getMappedOrderBy(OrderBy orderBy,
			Map<String, String> mapping) {
		if (orderBy == null) {
			return null;
		}
		String name = orderBy.getName();
		OrderDirection dir = orderBy.getDirection();
		if (mapping != null && mapping.containsKey(name)) {
			name = mapping.get(name);
		}
		return dir == OrderDirection.ASC ? OrderBy.asc(name) : OrderBy
				.desc(name);
	}
}